knitr::opts_chunk$set(echo = TRUE)
# Packages utilisés
library(config) # Utilisation d'un fichier de configuration, cf https://db.rstudio.com/best-practices/managing-credentials/#stored-in-a-file-with-config
library(tidyverse)
library(DBI) # pour les connexions aux BDD
library(RPostgreSQL) # driver postgres
library(RMariaDB) # driver MariaDB
library(RMySQL) # driver MySQL
library(lubridate) # calcul sur les dates
library(httr) #
library(jsonlite) # Gestion format json
library("readxl") # Lecture de fichiers xlsx
library(sf) # Traitement de données géoréférencées
library(mapview) # Affichage de données géoréférencées
library(plotly) # Graphiques interactifs
library(leaflet) # Cartes interactives
# Fonction pour requêter l'API Hub'Eau
get_hubeau <- function(path, query) {
response <- GET(url = path, query = query) %>%
content(as = "text", encoding = "UTF-8") %>%
fromJSON(flatten = TRUE)
data = response$data # Le jeu de données est dans l'objet "data"
if(response$count > query$size) { # Si la taille de page est plus petite que le nb de résultats, faire une boucle pour les pages suivantes
pages <- ceiling(response$count/query$size)
# Affichage d'une barre de progression
pb <- winProgressBar(title = "Récupération des chroniques", min = 0,
max = pages, width = 300)
for(i in 2:pages){
query$page <- i
response_i <- GET(url = path, query = query) %>%
content(as = "text", encoding = "UTF-8") %>%
fromJSON(flatten = TRUE)
data <- rbind(data, response_i$data) # Concaténation des lignes récupérées
setWinProgressBar(pb, i, title=paste( round(i/pages*100, 0), "% chargé")) # Avancement de la barre de progression
} # Fin boucle for
close(pb)
} # Fin if
return(data)
} # Fin get_hubeau
# Insertion de données
db.insertion <- function(con,table,data) {
# Date de la dernière donnée en base
Date_max <- tbl(con, table) %>% summarise(Date_max = max(Date_de_la_mesure, na.rm = TRUE)) %>% pull(Date_max)
if(is.na(Date_max)) Date_max <- as.Date(params$date_debut)-1
# Données plus récentes à insérer
data <- data %>% filter(Date_de_la_mesure > Date_max)
# Existe-t-il des données à insérer ?
if (isTRUE(data %>% tally() > 0)) {
# Insertion des nouvelles lignes
dbWriteTable(con, table, data, overwrite=FALSE, append=TRUE,
fileEncoding="latin1")
# Horodate en commentaire
mise_a_jour <- paste0(format.Date(Sys.Date(),"%d/%m/%Y"), " : Actualisation ",params$actualisation)
dbGetQuery(con, paste0("ALTER TABLE ",table," COMMENT = '",mise_a_jour,"';"))
paste0("Données insérées : ",data%>%tally)
}
else {
paste0("Aucune donnée à insérer depuis le ", format.Date(Date_max,"%d/%m/%Y"))
}
} # fin db.insertion
Connexion aux bases de données pour l’intégration des données importées
# Base Mariadb OEB - eau_tbi
con_eau_tbi <- dbConnect(RMariaDB::MariaDB(), default.file = '../../.my.cnf', groups="mysql_oeb",
dbname = "eau_tbi")
# Base MariaDB OEB - référentiels
con_referentiels <- dbConnect(RMariaDB::MariaDB(), default.file = '../../.my.cnf', groups="mysql_oeb",
dbname = "eau_referentiels")
# Base Postgres (développement)
conf <- config::get("postgres_dev")
con_postgresql_dev <- DBI::dbConnect(odbc::odbc(),
Driver = conf$driver,
servername = conf$server,
UID = conf$uid,
PWD = conf$pwd,
Port = conf$port,
database = 'eau',
encoding = "latin1")
Collecte des données de la base Naïades à partir de l’API Hydrobiologie du portail Hub’eau
Documentation de l’API : https://hubeau.eaufrance.fr/page/api-hydrobiologie
# Récupération des infos sur les stations de mesure
# Liste des paramètres de la requête
query_sites = list(
code_departement= params$num_departement,
format='json',
size=params$pagination
)
# Requête des stations
hubeau_sites <- get_hubeau(path = params$api_sites, query = query_sites)
# Conversion au format géographique
sites <- st_as_sf(hubeau_sites, coords = c("coordonnee_x", "coordonnee_y"),
crs = 2154, agr = "constant")
# Récupération des résultats d'indices biologiques
# Liste des paramètres de la requête (lot 1 de départements)
query_resultats = list(
#code_departement = params$num_departement,
code_departement = "29,22,35",
code_indice = params$codes_parametres,
#les paramètres date_debut et date_fin créent un bug dans l'API https://github.com/BRGM/hubeau/issues/81
#date_debut_prelevement = params$date_debut,
#date_fin_prelevement = params$date_fin,
size = params$pagination,
format = 'json')
# Requête des chroniques
hubeau_resultats <- get_hubeau(path = params$api_resultats, query=query_resultats)
# Liste des paramètres de la requête (lot 2 de départements)
query_resultats = list(
#code_departement = params$num_departement,
code_departement = "56,50,44,49,53",
code_indice = params$codes_parametres,
#les paramètres date_debut et date_fin créent un bug dans l'API https://github.com/BRGM/hubeau/issues/81
#date_debut_prelevement = params$date_debut,
#date_fin_prelevement = params$date_fin,
size = params$pagination,
format = 'json')
# Requête des chroniques
hubeau_resultats <- get_hubeau(path = params$api_resultats, query=query_resultats)%>%union(hubeau_resultats)
# Conversion au format géographique
resultats <- st_as_sf(hubeau_resultats, coords = c("coordonnee_x", "coordonnee_y"),
crs = 2154, agr = "constant")
Les stations de mesure et les chroniques d’analyses sont récupérées sur les départements 22,29,35,56,50,44,49,53 sur la période , via la fonction get_hubeau(path, query) définie ci-dessus.
- path : adresse de l’API
- query : liste des paramètres de requête avec leurs valeurs (code_)
Les départements séparés en deux lots pour respecter les contraintes de nb de lignes totales récupérées par requête.
Import des référentiels géographiques
Les polygones des territoires de SAGE bretons sont récupérées depuis le flux WFS de la DREAL Bretagne, ils correspondent au périmètre géographique des données à bancariser.
# couche des SAGEs bretons depuis Geobretagne
sages <- st_read("https://geobretagne.fr/geoserver/dreal_b/sage_dreal/wfs?SERVICE=WFS&REQUEST=GetCapabilities")
Reading layer `dreal_b:sage_dreal' from data source
`https://geobretagne.fr/geoserver/dreal_b/sage_dreal/wfs?SERVICE=WFS&REQUEST=GetCapabilities' using driver `WFS'
Simple feature collection with 21 features and 7 fields
Geometry type: MULTISURFACE
Dimension: XY
Bounding box: xmin: 123903.4 ymin: 6703536 xmax: 422117.4 ymax: 6882053
Projected CRS: RGF93 / Lambert-93
# pb pas moyen de manipuler cet objet => solution sur
# https://gis.stackexchange.com/questions/389814/r-st-centroid-geos-error-unknown-wkb-type-12/389854#389854
ensure_multipolygons <- function(X) {
tmp1 <- tempfile(fileext = ".gpkg")
tmp2 <- tempfile(fileext = ".gpkg")
st_write(X, tmp1)
gdalUtilities::ogr2ogr(tmp1, tmp2, f = "GPKG", nlt = "MULTIPOLYGON")
Y <- st_read(tmp2)
st_sf(st_drop_geometry(X), geom = st_geometry(Y))
}
sages <- ensure_multipolygons(sages)
Writing layer `file1678231e7beb' to data source `C:\Users\tbesse\AppData\Local\Temp\RtmpYrWJEH\file1678231e7beb.gpkg' using driver `GPKG'
Writing 21 features with 7 fields and geometry type Multi Surface.
Reading layer `file1678231e7beb' from data source `C:\Users\tbesse\AppData\Local\Temp\RtmpYrWJEH\file1678334e4c1c.gpkg' using driver `GPKG'
Simple feature collection with 21 features and 7 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: 123903.4 ymin: 6703536 xmax: 422117.4 ymax: 6882053
Projected CRS: RGF93 / Lambert-93
# couche des Hydroécorégions de niveau 2
her2 <- st_read("https://services.sandre.eaufrance.fr/geo/mdo?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&typename=Hydroecoregion2")%>%
st_transform(2154)
Reading layer `Hydroecoregion2' from data source
`https://services.sandre.eaufrance.fr/geo/mdo?SERVICE=WFS&VERSION=2.0.0&REQUEST=GetFeature&typename=Hydroecoregion2'
using driver `GML'
Simple feature collection with 114 features and 5 fields
Geometry type: MULTIPOLYGON
Dimension: XY
Bounding box: xmin: -4.79495 ymin: 41.36882 xmax: 9.560037 ymax: 51.08965
Geodetic CRS: WGS 84
Sélection des sites et des résultats sur les territoires des SAGE bretons
Les sites et résultats téléchargés sont sélectionnés sur le périmètre géographique retenu, par jointure avec la couche des SAGE.
sites_sages <- sites %>%
st_join(sages) %>%
filter(!is.na(cd_sage))
resultats_sages <- resultats %>%
st_join(sages) %>%
filter(!is.na(cd_sage)) %>%
st_join(her2)
Les sites font-ils partie du réseau RCS ?
L’information sur le réseau de suivi est incluse dans les données sur les sites fournies par l’API. Le réseau RCS correspond au code SANDRE : 0000000052
sites_rcs <- sites_sages %>%
# rowwise pour préciser que les opérations se font pour chaque ligne
rowwise()%>%
# Le code 0000000052 (RCS) est dans la liste des codes_réseaux
mutate(inclus_rcs = '0000000052' %in% unlist(codes_reseaux))%>%
select(code_station_hydrobio, inclus_rcs)
Carte des sites importés via Hub’eau, par département
sites_sages %>%
ggplot()+
geom_sf(data=sages)+
geom_sf(aes(color=libelle_departement))

Carte des résultats importés via Hub’eau, par qualification
resultats_sages %>%
ggplot()+
geom_sf(data=sages)+
geom_sf(aes(color=libelle_qualification))

Exemple de résulat : Carte de l’indice Diatomées (IBD)
resultats_sages %>%
filter(code_indice == '5856')%>% #5856 IBD Indice Diatomées
ggplot()+
geom_sf(data=sages)+
geom_sf(aes(color=resultat_indice))

Jointure avec les tables référentiels
Sites inconnus
Les sites importés sont comparés à la table des sites bancarisés précédemment, par code SANDRE.
Liste des sites inconnus :
sites_inconnus <- sites_sages %>%
# tables des sites
left_join(tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","site")), by=c("code_station_hydrobio" = "code"), copy=TRUE, suffix = c("", ".site"))%>%
filter(is.na(site_id))
sites_inconnus %>%
distinct(code_station_hydrobio, libelle_station_hydrobio)
Carte des sites inconnus :
sites_inconnus %>%
ggplot()+
geom_sf(data=sages)+
geom_sf(aes(color=libelle_departement))

Insertion des nouveaux sites
Table de correspondance site / territoires (EGA - Entités Geographiques et Administratives)
La table de correspondance est issue de la jointure spatiale entre la table des sites et les différentes échelles de territoires bretons : Région, Départements, EPCI, SAGE, Contrats de territoires
correspondance_site_ega <- tbl(con_postgresql_dev, dbplyr::in_schema("eau_referentiel","geo_correspondance_site_ega"))%>%
mutate(typesite = 'SITE')
correspondance_site_ega
Liste des paramètres bancarisés
La liste des paramètres est issue du dictionnaire national des données sur l’eau (SANDRE).
liste_parametres <- as.list(strsplit(params$codes_parametres, ",")[[1]])
parametres <- tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","parametre"))%>%
collect() %>%
filter(code %in% liste_parametres)
parametres
Table complète
Table des résultats avec les attributs des référentiels
table_indices <- resultats_sages %>%
mutate(CoordX_WGS84 = st_coordinates(st_transform(geometry, 4326))[,1],
CoordY_WGS84 = st_coordinates(st_transform(geometry, 4326))[,2],
date_prelevement = as.Date(date_prelevement)
)%>%
as_tibble()%>%
# tables des paramètres
left_join(parametres, by=c("code_indice"="code"), suffix = c("", ".parametre"))%>%
# Unité inconnue --> code sandre 0
# n (nombre) --> code sandre 214
# ‰ vs SMOW --> code sandre 32
mutate(unite_code = case_when(unite_indice == "Unité inconnue" ~ '0',
unite_indice == "n" ~ '214',
unite_indice == "‰ vs SMOW" ~ '32',
TRUE ~ unite_indice),
resultat_indice = ifelse(resultat_indice == 999, NA, resultat_indice)) %>%
# tables des unités
left_join(tbl(con_postgresql_dev, dbplyr::in_schema("eau_referentiel","unite")), by=c("unite_code" = "code"), copy=TRUE, suffix = c("", ".unite"))%>%
# tables des sites
left_join(tbl(con_postgresql_dev, dbplyr::in_schema("eau_structure","site")), by=c("code_station_hydrobio" = "code"), copy=TRUE, suffix = c("", ".site"))
table_indices
Synthèse des données manquantes
# Synthèse des données manquantes
table_indices %>%
summarise(
nb_lignes = n(),
lignes_sans_parametre = sum(is.na(parametre_id)),
lignes_sans_unite = sum(is.na(unite_id)),
lignes_sans_sites = sum(is.na(site_id))
)
# Unités inconnues de la table de référence
table_indices %>% filter(is.na(unite_id)) %>% distinct(unite_indice)
# Stations inconnues de la table des sites
table_indices %>% filter(is.na(site_id)) %>% distinct(libelle_station_hydrobio)
# Liste des codes indices
table_indices %>% distinct(code_indice)
NA
Classes des indices
Attribution des classes de qualité aux résultats par paramètre (indice biologique)
table_indices_classes <- table_indices %>%
left_join(classes_qualite, by = c("parametre_id"), copy = TRUE, suffix=c("",".classe")) %>%
filter(resultat_indice < borne_sup_exclue & resultat_indice >= borne_inf_inclue)
table_indices_classes
Exploration des données
Données disponibles par an et par indice
table_indices_classes %>%
group_by(year(date_prelevement), libelle_support, libelle_indice) %>%
summarise(Nb_resultats = n(),
Annee = year(date_prelevement)) %>%
ggplot(aes(x = as.factor(Annee), y=Nb_resultats, fill=libelle_indice))+
geom_bar(stat="identity")+
facet_grid(libelle_support~.)
`summarise()` has grouped output by 'year(date_prelevement)', 'libelle_support', 'libelle_indice'. You can override using the `.groups` argument.

NA
Distribution des résultats par indice et par classe de qualité
table_indices_classes %>%
arrange(code.x)%>%
ggplot(aes(x = code.x, y=resultat_indice, fill = code.x))+
geom_boxplot()+
facet_wrap(~libelle_indice, scales = "free")

table_indices_classes
Import des données en base
Données au format de la table eau_structure.analyse_bio_esu - serveur PostgreSQL DEV
Sélection des nouvelles lignes à insérer
insert_analyse_bio_esu <- analyse_bio_esu
Insertion des nouvelles lignes
Données au format de la table eau_tbi.oeb_eau_qualite_biologique_ce - serveur MariaDB OEB
table_series_indices <- table_indices_classes_annee %>%
mutate(indice = paste(libelle, code_indice, sep=' - '))%>%
pivot_longer(cols= c("classe","resultat_indice","resultat_qualification"))%>%
mutate(Serie = case_when(name == 'classe' ~ paste('Classe',libelle_support,sep = ' - '),
name == 'resultat_indice' ~ paste('Indice',indice,sep = ' - '),
name == 'resultat_qualification' ~ paste('Qualification',indice,sep = ' - ')
)
)%>%
group_by(code_station_hydrobio,
longitude_wgs84,
latitude_wgs84,
libelle_support,
Serie,
symbole,
Annee)%>%
summarise(Resultat = max(value))%>%
ungroup()%>%
union(table_indices_classe_globale_annee)
`summarise()` has grouped output by 'code_station_hydrobio', 'longitude_wgs84', 'latitude_wgs84', 'libelle_support', 'Serie', 'symbole'. You can override using the `.groups` argument.
table_series_indices
Déclinaison par combinaison SITE / EGA
La table de valorisation décline les résultats pour chaque échelle de territoire
table_series_indices_ega <- table_series_indices%>%
# table des correspondances sites / UGA
left_join(correspondance_site_ega, by=c("code_station_hydrobio" = "cdsite"), copy = TRUE)%>%
left_join(sites_rcs, by="code_station_hydrobio")
table_series_indices_ega
Sélection des lignes nouvelles à insérer
La table de résultats est comparée aux données déjà bancarisées par une jointure d’exclusion (anti_join). Les lignes restantes peuvent être insérées.
insert_oeb_eau_qualite_biologique_ce <- oeb_eau_qualite_biologique_ce %>%
ungroup() %>%
# Retirer les lignes des résultats déjà existants dans la table
anti_join(tbl(con_eau_tbi,"oeb_eau_qualite_biologique_ce", by=c("Type_entitite_geographique", "Code_entitite_geographique", "Type_entitite_geographique_associee", "Code_entitite_geographique_associee", "Periode", "Serie")), copy = TRUE)%>%
# Retirer les stations hydro sans EGA identifiée
filter(!is.na(Type_entitite_geographique))%>%
# Retirer les lignes avec une valeur NA
drop_na()
Joining, by = c("Type_entitite_geographique", "Code_entitite_geographique", "Libelle_entitite_geographique", "CoordX_WGS84", "CoordY_WGS84", "Reseau_RCS", "Type_entitite_geographique_associee", "Code_entitite_geographique_associee", "Libelle_entitite_geographique_associee", "Periode", "Serie", "unite", "Resultat", "Source", "Mise_a_jour")
insert_oeb_eau_qualite_biologique_ce
Insertion des nouvelles lignes
Exports CSV pour la publication du jeu de données sur les portails opendata
Export pour le GIDE
Fichier pour la publication du jeu de données sur le portail OEB
# Depuis la base de données
#tbl(con_eau_tbi, "oeb_eau_qualite_biologique_ce_new")%>%
# ou depuis la table en mémoire
oeb_eau_qualite_biologique_ce %>%
write.table(file = paste0(params$path_dataviz,"\\GIDE\\","oeb_eau_qualite_biologique_ce.csv"), quote = TRUE, sep = ";",
eol = "\n", na = "", dec = ",",
fileEncoding = "UTF-8")
Export pour GEOB
Fichier pour la publication du jeu de données sur Geobretagne
Export pour le paramètre “Qualité globale”
oeb_eau_qualite_geob %>%
filter(Serie == 'Classe - Qualité biologique Globale') %>%
write.table(file = paste0(params$path_dataviz,"\\GEOB\\","oeb_eau_qualite_biologique_globale.csv"), quote = TRUE, sep = ";",
eol = "\n", na = "", dec = ",",
fileEncoding = "UTF-8")
Export pour le paramètre “Diatomées”
oeb_eau_qualite_geob %>%
filter(Serie == 'Classe - Diatomées benthiques') %>%
write.table(file = paste0(params$path_dataviz,"\\GEOB\\","oeb_eau_qualite_diatomees.csv"), quote = TRUE, sep = ";",
eol = "\n", na = "", dec = ",",
fileEncoding = "UTF-8")
Export pour le paramètre “Macroinvertébrés”
oeb_eau_qualite_geob %>%
filter(Serie == 'Classe - Macroinvertébrés aquatiques') %>%
write.table(file = paste0(params$path_dataviz,"\\GEOB\\","oeb_eau_qualite_macroinvertebres.csv"), quote = TRUE, sep = ";",
eol = "\n", na = "", dec = ",",
fileEncoding = "UTF-8")
Export pour le paramètre “Macrophytes”
oeb_eau_qualite_geob %>%
filter(Serie == 'Classe - Macrophytes') %>%
write.table(file = paste0(params$path_dataviz,"\\GEOB\\","oeb_eau_qualite_macrophytes.csv"), quote = TRUE, sep = ";",
eol = "\n", na = "", dec = ",",
fileEncoding = "UTF-8")
LS0tDQp0aXRsZTogIkludGVncmF0aW9uIGRlcyBkb25uw6llcyBoeWRyb2Jpb2xvZ2lxdWVzIg0Kb3V0cHV0OiBodG1sX2RvY3VtZW50DQpwYXJhbXM6DQogIGFwaV9zaXRlczogaHR0cHM6Ly9odWJlYXUuZWF1ZnJhbmNlLmZyL2FwaS92YmV0YS9oeWRyb2Jpby9zdGF0aW9uc19oeWRyb2Jpbz8NCiAgYXBpX3Jlc3VsdGF0czogaHR0cHM6Ly9odWJlYXUuZWF1ZnJhbmNlLmZyL2FwaS92YmV0YS9oeWRyb2Jpby9pbmRpY2VzPw0KICBudW1fZGVwYXJ0ZW1lbnQ6ICcyMiwyOSwzNSw1Niw1MCw0NCw0OSw1MycNCiAgY29kZXNfcGFyYW1ldHJlczogJzcwMzYsNTkxMCwxMDIyLDU4NTYsMjkyOCw2OTU5LDY5NTEsNjk1NSwxMDAwLDI1MjcnDQogIGRhdGVfZGVidXQ6ICcyMDE3LTAxLTAxJw0KICBkYXRlX2ZpbjogJzIwMTktMTItMzEnDQogIHBhZ2luYXRpb246IDUwMDANCiAgcGF0aF9kYXRhdml6OiBPOlwwNC5EQVRBVklTVUFMSVNBVElPTlxJTkRJQ0FURVVSU19CSU9MT0dJRVxEQ0VfRVRBVF9CSU9MT0dJUVVFDQotLS0NCg0KYGBge3Igc2V0dXB9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQoNCiMgUGFja2FnZXMgdXRpbGlzw6lzDQpsaWJyYXJ5KGNvbmZpZykgIyBVdGlsaXNhdGlvbiBkJ3VuIGZpY2hpZXIgZGUgY29uZmlndXJhdGlvbiwgY2YgIGh0dHBzOi8vZGIucnN0dWRpby5jb20vYmVzdC1wcmFjdGljZXMvbWFuYWdpbmctY3JlZGVudGlhbHMvI3N0b3JlZC1pbi1hLWZpbGUtd2l0aC1jb25maWcNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShEQkkpICMgcG91ciBsZXMgY29ubmV4aW9ucyBhdXggQkREDQpsaWJyYXJ5KFJQb3N0Z3JlU1FMKSAjIGRyaXZlciBwb3N0Z3Jlcw0KbGlicmFyeShSTWFyaWFEQikgIyBkcml2ZXIgTWFyaWFEQg0KbGlicmFyeShSTXlTUUwpICMgZHJpdmVyIE15U1FMDQpsaWJyYXJ5KGx1YnJpZGF0ZSkgIyBjYWxjdWwgc3VyIGxlcyBkYXRlcw0KDQpsaWJyYXJ5KGh0dHIpICMNCmxpYnJhcnkoanNvbmxpdGUpICMgR2VzdGlvbiBmb3JtYXQganNvbg0KDQpsaWJyYXJ5KCJyZWFkeGwiKSAjIExlY3R1cmUgZGUgZmljaGllcnMgeGxzeA0KDQpsaWJyYXJ5KHNmKSAjIFRyYWl0ZW1lbnQgZGUgZG9ubsOpZXMgZ8Opb3LDqWbDqXJlbmPDqWVzDQpsaWJyYXJ5KG1hcHZpZXcpICMgQWZmaWNoYWdlIGRlIGRvbm7DqWVzIGfDqW9yw6lmw6lyZW5jw6llcw0KbGlicmFyeShwbG90bHkpICMgR3JhcGhpcXVlcyBpbnRlcmFjdGlmcw0KbGlicmFyeShsZWFmbGV0KSAjIENhcnRlcyBpbnRlcmFjdGl2ZXMNCg0KIyBGb25jdGlvbiBwb3VyIHJlcXXDqnRlciBsJ0FQSSBIdWInRWF1DQoNCmdldF9odWJlYXUgPC0gZnVuY3Rpb24ocGF0aCwgcXVlcnkpIHsNCiAgDQogIHJlc3BvbnNlIDwtIEdFVCh1cmwgPSBwYXRoLCBxdWVyeSA9IHF1ZXJ5KSAlPiUgDQogIGNvbnRlbnQoYXMgPSAidGV4dCIsIGVuY29kaW5nID0gIlVURi04IikgJT4lDQogIGZyb21KU09OKGZsYXR0ZW4gPSBUUlVFKQ0KICANCmRhdGEgPSByZXNwb25zZSRkYXRhICMgTGUgamV1IGRlIGRvbm7DqWVzIGVzdCBkYW5zIGwnb2JqZXQgImRhdGEiDQoNCmlmKHJlc3BvbnNlJGNvdW50ID4gcXVlcnkkc2l6ZSkgeyAjIFNpIGxhIHRhaWxsZSBkZSBwYWdlIGVzdCBwbHVzIHBldGl0ZSBxdWUgbGUgbmIgZGUgcsOpc3VsdGF0cywgZmFpcmUgdW5lIGJvdWNsZSBwb3VyIGxlcyBwYWdlcyBzdWl2YW50ZXMNCiAgDQpwYWdlcyA8LSBjZWlsaW5nKHJlc3BvbnNlJGNvdW50L3F1ZXJ5JHNpemUpDQoNCiMgQWZmaWNoYWdlIGQndW5lIGJhcnJlIGRlIHByb2dyZXNzaW9uDQpwYiA8LSB3aW5Qcm9ncmVzc0Jhcih0aXRsZSA9ICJSw6ljdXDDqXJhdGlvbiBkZXMgY2hyb25pcXVlcyIsIG1pbiA9IDAsDQogICAgICAgICAgICAgICAgICAgICBtYXggPSBwYWdlcywgd2lkdGggPSAzMDApDQoNCmZvcihpIGluIDI6cGFnZXMpew0KICANCiAgcXVlcnkkcGFnZSA8LSBpDQogIA0KICByZXNwb25zZV9pIDwtIEdFVCh1cmwgPSBwYXRoLCBxdWVyeSA9IHF1ZXJ5KSAlPiUgDQogIGNvbnRlbnQoYXMgPSAidGV4dCIsIGVuY29kaW5nID0gIlVURi04IikgJT4lDQogIGZyb21KU09OKGZsYXR0ZW4gPSBUUlVFKQ0KDQpkYXRhIDwtIHJiaW5kKGRhdGEsIHJlc3BvbnNlX2kkZGF0YSkgIyBDb25jYXTDqW5hdGlvbiBkZXMgbGlnbmVzIHLDqWN1cMOpcsOpZXMNCg0Kc2V0V2luUHJvZ3Jlc3NCYXIocGIsIGksIHRpdGxlPXBhc3RlKCByb3VuZChpL3BhZ2VzKjEwMCwgMCksICIlIGNoYXJnw6kiKSkgIyBBdmFuY2VtZW50IGRlIGxhIGJhcnJlIGRlIHByb2dyZXNzaW9uDQoNCn0gIyBGaW4gYm91Y2xlIGZvcg0KDQpjbG9zZShwYikNCg0KfSAjIEZpbiBpZg0KDQpyZXR1cm4oZGF0YSkNCiAgfSAjIEZpbiBnZXRfaHViZWF1DQoNCiMgSW5zZXJ0aW9uIGRlIGRvbm7DqWVzDQoNCmRiLmluc2VydGlvbiA8LSBmdW5jdGlvbihjb24sdGFibGUsZGF0YSkgew0KICANCiAgIyBEYXRlIGRlIGxhIGRlcm5pw6hyZSBkb25uw6llIGVuIGJhc2UNCiAgRGF0ZV9tYXggPC0gdGJsKGNvbiwgdGFibGUpICU+JSBzdW1tYXJpc2UoRGF0ZV9tYXggPSBtYXgoRGF0ZV9kZV9sYV9tZXN1cmUsIG5hLnJtID0gVFJVRSkpICU+JSBwdWxsKERhdGVfbWF4KQ0KICBpZihpcy5uYShEYXRlX21heCkpIERhdGVfbWF4IDwtIGFzLkRhdGUocGFyYW1zJGRhdGVfZGVidXQpLTENCiAgDQogICMgRG9ubsOpZXMgcGx1cyByw6ljZW50ZXMgw6AgaW5zw6lyZXINCiAgZGF0YSA8LSBkYXRhICU+JSBmaWx0ZXIoRGF0ZV9kZV9sYV9tZXN1cmUgPiBEYXRlX21heCkNCiAgDQogICMgRXhpc3RlLXQtaWwgZGVzIGRvbm7DqWVzIMOgIGluc8OpcmVyID8NCmlmIChpc1RSVUUoZGF0YSAlPiUgdGFsbHkoKSA+IDApKSB7DQogIA0KICAjIEluc2VydGlvbiBkZXMgbm91dmVsbGVzIGxpZ25lcw0KICBkYldyaXRlVGFibGUoY29uLCB0YWJsZSwgZGF0YSwgb3ZlcndyaXRlPUZBTFNFLCBhcHBlbmQ9VFJVRSwNCiAgICAgICAgICAgICBmaWxlRW5jb2Rpbmc9ImxhdGluMSIpDQogIA0KICAjIEhvcm9kYXRlIGVuIGNvbW1lbnRhaXJlDQptaXNlX2Ffam91ciA8LSBwYXN0ZTAoZm9ybWF0LkRhdGUoU3lzLkRhdGUoKSwiJWQvJW0vJVkiKSwgIiA6IEFjdHVhbGlzYXRpb24gIixwYXJhbXMkYWN0dWFsaXNhdGlvbikNCg0KZGJHZXRRdWVyeShjb24sIHBhc3RlMCgiQUxURVIgVEFCTEUgIix0YWJsZSwiIENPTU1FTlQgPSAnIixtaXNlX2Ffam91ciwiJzsiKSkNCg0KcGFzdGUwKCJEb25uw6llcyBpbnPDqXLDqWVzIDogIixkYXRhJT4ldGFsbHkpDQoNCn0NCiAgZWxzZSB7DQogICAgDQogICAgcGFzdGUwKCJBdWN1bmUgZG9ubsOpZSDDoCBpbnPDqXJlciBkZXB1aXMgbGUgIiwgZm9ybWF0LkRhdGUoRGF0ZV9tYXgsIiVkLyVtLyVZIikpDQogIA0KICAgIH0NCiAgDQp9ICMgZmluIGRiLmluc2VydGlvbg0KYGBgDQoNCiMgQ29ubmV4aW9uIGF1eCBiYXNlcyBkZSBkb25uw6llcyBwb3VyIGwnaW50w6lncmF0aW9uIGRlcyBkb25uw6llcyBpbXBvcnTDqWVzDQoNCmBgYHtyIGNvbm5leGlvbl9iZH0NCg0KIyBCYXNlIE1hcmlhZGIgT0VCIC0gZWF1X3RiaQ0KY29uX2VhdV90YmkgPC0gZGJDb25uZWN0KFJNYXJpYURCOjpNYXJpYURCKCksIGRlZmF1bHQuZmlsZSA9ICcuLi8uLi8ubXkuY25mJywgZ3JvdXBzPSJteXNxbF9vZWIiLA0KZGJuYW1lID0gImVhdV90YmkiKQ0KDQojIEJhc2UgTWFyaWFEQiBPRUIgLSByw6lmw6lyZW50aWVscw0KY29uX3JlZmVyZW50aWVscyA8LSBkYkNvbm5lY3QoUk1hcmlhREI6Ok1hcmlhREIoKSwgZGVmYXVsdC5maWxlID0gJy4uLy4uLy5teS5jbmYnLCBncm91cHM9Im15c3FsX29lYiIsDQpkYm5hbWUgPSAiZWF1X3JlZmVyZW50aWVscyIpDQoNCiMgQmFzZSBQb3N0Z3JlcyAoZMOpdmVsb3BwZW1lbnQpDQoNCmNvbmYgPC0gY29uZmlnOjpnZXQoInBvc3RncmVzX2RldiIpDQoNCmNvbl9wb3N0Z3Jlc3FsX2RldiA8LSBEQkk6OmRiQ29ubmVjdChvZGJjOjpvZGJjKCksDQogICAgICAgICAgICAgICAgICAgICAgICAgIERyaXZlciAgICAgICA9IGNvbmYkZHJpdmVyLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBzZXJ2ZXJuYW1lICAgPSBjb25mJHNlcnZlciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgVUlEID0gY29uZiR1aWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgIFBXRCA9IGNvbmYkcHdkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICBQb3J0ID0gY29uZiRwb3J0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhYmFzZSA9ICdlYXUnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmNvZGluZyA9ICJsYXRpbjEiKQ0KYGBgDQojIENvbGxlY3RlIGRlcyBkb25uw6llcyBkZSBsYSBiYXNlIE5hw69hZGVzIMOgIHBhcnRpciBkZSBsJ0FQSSBIeWRyb2Jpb2xvZ2llIGR1IHBvcnRhaWwgSHViJ2VhdQ0KDQoqKkRvY3VtZW50YXRpb24gZGUgbCdBUEkgOioqIGh0dHBzOi8vaHViZWF1LmVhdWZyYW5jZS5mci9wYWdlL2FwaS1oeWRyb2Jpb2xvZ2llDQoNCmBgYHtyIGh1YmVhdX0NCg0KIyBSw6ljdXDDqXJhdGlvbiBkZXMgaW5mb3Mgc3VyIGxlcyBzdGF0aW9ucyBkZSBtZXN1cmUNCg0KIyBMaXN0ZSBkZXMgcGFyYW3DqHRyZXMgZGUgbGEgcmVxdcOqdGUNCnF1ZXJ5X3NpdGVzID0gbGlzdCgNCiAgY29kZV9kZXBhcnRlbWVudD0gcGFyYW1zJG51bV9kZXBhcnRlbWVudCwNCiAgZm9ybWF0PSdqc29uJywNCiAgc2l6ZT1wYXJhbXMkcGFnaW5hdGlvbg0KICApDQoNCiMgUmVxdcOqdGUgZGVzIHN0YXRpb25zDQpodWJlYXVfc2l0ZXMgPC0gZ2V0X2h1YmVhdShwYXRoID0gcGFyYW1zJGFwaV9zaXRlcywgcXVlcnkgPSBxdWVyeV9zaXRlcykNCg0KIyBDb252ZXJzaW9uIGF1IGZvcm1hdCBnw6lvZ3JhcGhpcXVlDQpzaXRlcyA8LSBzdF9hc19zZihodWJlYXVfc2l0ZXMsIGNvb3JkcyA9IGMoImNvb3Jkb25uZWVfeCIsICJjb29yZG9ubmVlX3kiKSwgDQogICAgY3JzID0gMjE1NCwgYWdyID0gImNvbnN0YW50IikNCg0KIyBSw6ljdXDDqXJhdGlvbiBkZXMgcsOpc3VsdGF0cyBkJ2luZGljZXMgYmlvbG9naXF1ZXMNCg0KIyBMaXN0ZSBkZXMgcGFyYW3DqHRyZXMgZGUgbGEgcmVxdcOqdGUgKGxvdCAxIGRlIGTDqXBhcnRlbWVudHMpDQpxdWVyeV9yZXN1bHRhdHMgPSBsaXN0KA0KICAgICAgICAgICAgICAgICAjY29kZV9kZXBhcnRlbWVudCA9IHBhcmFtcyRudW1fZGVwYXJ0ZW1lbnQsDQogICAgICAgICAgICAgICAgIGNvZGVfZGVwYXJ0ZW1lbnQgPSAiMjksMjIsMzUiLA0KICAgICAgICAgICAgICAgICBjb2RlX2luZGljZSA9IHBhcmFtcyRjb2Rlc19wYXJhbWV0cmVzLA0KICAgICAgICAgICAgICAgICAjbGVzIHBhcmFtw6h0cmVzIGRhdGVfZGVidXQgZXQgZGF0ZV9maW4gY3LDqWVudCB1biBidWcgZGFucyBsJ0FQSSBodHRwczovL2dpdGh1Yi5jb20vQlJHTS9odWJlYXUvaXNzdWVzLzgxDQogICAgICAgICAgICAgICAgICNkYXRlX2RlYnV0X3ByZWxldmVtZW50ID0gcGFyYW1zJGRhdGVfZGVidXQsDQogICAgICAgICAgICAgICAgICNkYXRlX2Zpbl9wcmVsZXZlbWVudCA9IHBhcmFtcyRkYXRlX2ZpbiwNCiAgICAgICAgICAgICAgICAgc2l6ZSA9IHBhcmFtcyRwYWdpbmF0aW9uLA0KICAgICAgICAgICAgICAgICBmb3JtYXQgPSAnanNvbicpDQoNCiMgUmVxdcOqdGUgZGVzIGNocm9uaXF1ZXMNCmh1YmVhdV9yZXN1bHRhdHMgPC0gZ2V0X2h1YmVhdShwYXRoID0gcGFyYW1zJGFwaV9yZXN1bHRhdHMsIHF1ZXJ5PXF1ZXJ5X3Jlc3VsdGF0cykNCg0KIyBMaXN0ZSBkZXMgcGFyYW3DqHRyZXMgZGUgbGEgcmVxdcOqdGUgKGxvdCAyIGRlIGTDqXBhcnRlbWVudHMpDQpxdWVyeV9yZXN1bHRhdHMgPSBsaXN0KA0KICAgICAgICAgICAgICAgICAjY29kZV9kZXBhcnRlbWVudCA9IHBhcmFtcyRudW1fZGVwYXJ0ZW1lbnQsDQogICAgICAgICAgICAgICAgIGNvZGVfZGVwYXJ0ZW1lbnQgPSAiNTYsNTAsNDQsNDksNTMiLA0KICAgICAgICAgICAgICAgICBjb2RlX2luZGljZSA9IHBhcmFtcyRjb2Rlc19wYXJhbWV0cmVzLA0KICAgICAgICAgICAgICAgICAjbGVzIHBhcmFtw6h0cmVzIGRhdGVfZGVidXQgZXQgZGF0ZV9maW4gY3LDqWVudCB1biBidWcgZGFucyBsJ0FQSSBodHRwczovL2dpdGh1Yi5jb20vQlJHTS9odWJlYXUvaXNzdWVzLzgxDQogICAgICAgICAgICAgICAgICNkYXRlX2RlYnV0X3ByZWxldmVtZW50ID0gcGFyYW1zJGRhdGVfZGVidXQsDQogICAgICAgICAgICAgICAgICNkYXRlX2Zpbl9wcmVsZXZlbWVudCA9IHBhcmFtcyRkYXRlX2ZpbiwNCiAgICAgICAgICAgICAgICAgc2l6ZSA9IHBhcmFtcyRwYWdpbmF0aW9uLA0KICAgICAgICAgICAgICAgICBmb3JtYXQgPSAnanNvbicpDQoNCiMgUmVxdcOqdGUgZGVzIGNocm9uaXF1ZXMNCmh1YmVhdV9yZXN1bHRhdHMgPC0gZ2V0X2h1YmVhdShwYXRoID0gcGFyYW1zJGFwaV9yZXN1bHRhdHMsIHF1ZXJ5PXF1ZXJ5X3Jlc3VsdGF0cyklPiV1bmlvbihodWJlYXVfcmVzdWx0YXRzKQ0KDQojIENvbnZlcnNpb24gYXUgZm9ybWF0IGfDqW9ncmFwaGlxdWUNCnJlc3VsdGF0cyA8LSBzdF9hc19zZihodWJlYXVfcmVzdWx0YXRzLCBjb29yZHMgPSBjKCJjb29yZG9ubmVlX3giLCAiY29vcmRvbm5lZV95IiksIA0KICAgIGNycyA9IDIxNTQsIGFnciA9ICJjb25zdGFudCIpDQpgYGANCkxlcyBzdGF0aW9ucyBkZSBtZXN1cmUgZXQgbGVzIGNocm9uaXF1ZXMgZCdhbmFseXNlcyBzb250IHLDqWN1cMOpcsOpZXMgc3VyIGxlcyBkw6lwYXJ0ZW1lbnRzIGByIHBhcmFtcyRudW1fZGVwYXJ0ZW1lbnRgIHN1ciBsYSBww6lyaW9kZSAsIHZpYSBsYSBmb25jdGlvbiAqZ2V0X2h1YmVhdShwYXRoLCBxdWVyeSkqIGTDqWZpbmllIGNpLWRlc3N1cy4NCg0KLSBwYXRoIDogYWRyZXNzZSBkZSBsJ0FQSQ0KLSBxdWVyeSA6IGxpc3RlIGRlcyBwYXJhbcOodHJlcyBkZSByZXF1w6p0ZSBhdmVjIGxldXJzIHZhbGV1cnMgKGNvZGVfKQ0KDQpMZXMgZMOpcGFydGVtZW50cyBzw6lwYXLDqXMgZW4gZGV1eCBsb3RzIHBvdXIgcmVzcGVjdGVyIGxlcyBjb250cmFpbnRlcyBkZSBuYiBkZSBsaWduZXMgdG90YWxlcyByw6ljdXDDqXLDqWVzIHBhciByZXF1w6p0ZS4NCg0KIyBJbXBvcnQgZGVzIHLDqWbDqXJlbnRpZWxzIGfDqW9ncmFwaGlxdWVzDQoNCkxlcyBwb2x5Z29uZXMgZGVzIHRlcnJpdG9pcmVzIGRlIFNBR0UgYnJldG9ucyBzb250IHLDqWN1cMOpcsOpZXMgZGVwdWlzIGxlIGZsdXggV0ZTIGRlIGxhIERSRUFMIEJyZXRhZ25lLCBpbHMgY29ycmVzcG9uZGVudCBhdSBww6lyaW3DqHRyZSBnw6lvZ3JhcGhpcXVlIGRlcyBkb25uw6llcyDDoCBiYW5jYXJpc2VyLg0KDQpgYGB7ciBjb3VjaGVfc2FnZXN9DQojIGNvdWNoZSBkZXMgU0FHRXMgYnJldG9ucyBkZXB1aXMgR2VvYnJldGFnbmUNCnNhZ2VzIDwtIHN0X3JlYWQoImh0dHBzOi8vZ2VvYnJldGFnbmUuZnIvZ2Vvc2VydmVyL2RyZWFsX2Ivc2FnZV9kcmVhbC93ZnM/U0VSVklDRT1XRlMmUkVRVUVTVD1HZXRDYXBhYmlsaXRpZXMiKQ0KDQojIHBiIHBhcyBtb3llbiBkZSBtYW5pcHVsZXIgY2V0IG9iamV0ID0+IHNvbHV0aW9uIHN1cg0KIyBodHRwczovL2dpcy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMzg5ODE0L3Itc3QtY2VudHJvaWQtZ2Vvcy1lcnJvci11bmtub3duLXdrYi10eXBlLTEyLzM4OTg1NCMzODk4NTQNCmVuc3VyZV9tdWx0aXBvbHlnb25zIDwtIGZ1bmN0aW9uKFgpIHsNCiAgdG1wMSA8LSB0ZW1wZmlsZShmaWxlZXh0ID0gIi5ncGtnIikNCiAgdG1wMiA8LSB0ZW1wZmlsZShmaWxlZXh0ID0gIi5ncGtnIikNCiAgc3Rfd3JpdGUoWCwgdG1wMSkNCiAgZ2RhbFV0aWxpdGllczo6b2dyMm9ncih0bXAxLCB0bXAyLCBmID0gIkdQS0ciLCBubHQgPSAiTVVMVElQT0xZR09OIikNCiAgWSA8LSBzdF9yZWFkKHRtcDIpDQogIHN0X3NmKHN0X2Ryb3BfZ2VvbWV0cnkoWCksIGdlb20gPSBzdF9nZW9tZXRyeShZKSkNCn0NCg0Kc2FnZXMgPC0gZW5zdXJlX211bHRpcG9seWdvbnMoc2FnZXMpDQoNCiMgY291Y2hlIGRlcyBIeWRyb8OpY29yw6lnaW9ucyBkZSBuaXZlYXUgMg0KDQpoZXIyIDwtIHN0X3JlYWQoImh0dHBzOi8vc2VydmljZXMuc2FuZHJlLmVhdWZyYW5jZS5mci9nZW8vbWRvP1NFUlZJQ0U9V0ZTJlZFUlNJT049Mi4wLjAmUkVRVUVTVD1HZXRGZWF0dXJlJnR5cGVuYW1lPUh5ZHJvZWNvcmVnaW9uMiIpJT4lDQogIHN0X3RyYW5zZm9ybSgyMTU0KQ0KYGBgDQojIFPDqWxlY3Rpb24gZGVzIHNpdGVzIGV0IGRlcyByw6lzdWx0YXRzIHN1ciBsZXMgdGVycml0b2lyZXMgZGVzIFNBR0UgYnJldG9ucw0KDQpMZXMgc2l0ZXMgZXQgcsOpc3VsdGF0cyB0w6lsw6ljaGFyZ8OpcyBzb250IHPDqWxlY3Rpb25uw6lzIHN1ciBsZSBww6lyaW3DqHRyZSBnw6lvZ3JhcGhpcXVlIHJldGVudSwgcGFyIGpvaW50dXJlIGF2ZWMgbGEgY291Y2hlIGRlcyBTQUdFLg0KDQpgYGB7ciBzZWxlY3Rpb24gc3VyIGxlcyB0ZXJyaXRvaXJlcyBkZXMgU0FHRXN9DQoNCnNpdGVzX3NhZ2VzIDwtIHNpdGVzICU+JSANCiAgc3Rfam9pbihzYWdlcykgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNkX3NhZ2UpKQ0KDQpyZXN1bHRhdHNfc2FnZXMgPC0gcmVzdWx0YXRzICU+JSANCiAgc3Rfam9pbihzYWdlcykgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGNkX3NhZ2UpKSAlPiUgDQogIHN0X2pvaW4oaGVyMikNCg0KYGBgDQoNCiMjIExlcyBzaXRlcyBmb250LWlscyBwYXJ0aWUgZHUgcsOpc2VhdSBSQ1MgPw0KDQpMJ2luZm9ybWF0aW9uIHN1ciBsZSByw6lzZWF1IGRlIHN1aXZpIGVzdCBpbmNsdXNlIGRhbnMgbGVzIGRvbm7DqWVzIHN1ciBsZXMgc2l0ZXMgZm91cm5pZXMgcGFyIGwnQVBJLiBMZSByw6lzZWF1IFJDUyBjb3JyZXNwb25kIGF1IGNvZGUgU0FORFJFIDogMDAwMDAwMDA1Mg0KDQpgYGB7ciBzaXRlc19yY3N9DQpzaXRlc19yY3MgPC0gc2l0ZXNfc2FnZXMgJT4lDQogICMgcm93d2lzZSBwb3VyIHByw6ljaXNlciBxdWUgbGVzIG9ww6lyYXRpb25zIHNlIGZvbnQgcG91ciBjaGFxdWUgbGlnbmUNCiAgcm93d2lzZSgpJT4lDQogICMgTGUgY29kZSAwMDAwMDAwMDUyIChSQ1MpIGVzdCBkYW5zIGxhIGxpc3RlIGRlcyBjb2Rlc19yw6lzZWF1eA0KICBtdXRhdGUoaW5jbHVzX3JjcyA9ICcwMDAwMDAwMDUyJyAlaW4lIHVubGlzdChjb2Rlc19yZXNlYXV4KSklPiUNCiAgc2VsZWN0KGNvZGVfc3RhdGlvbl9oeWRyb2JpbywgaW5jbHVzX3JjcykNCmBgYA0KDQojIyBDYXJ0ZSBkZXMgc2l0ZXMgaW1wb3J0w6lzIHZpYSBIdWInZWF1LCBwYXIgZMOpcGFydGVtZW50DQoNCmBgYHtyIGNhcnRlX3NpdGVzfQ0Kc2l0ZXNfc2FnZXMgJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGE9c2FnZXMpKw0KZ2VvbV9zZihhZXMoY29sb3I9bGliZWxsZV9kZXBhcnRlbWVudCkpDQpgYGANCiMjIENhcnRlIGRlcyByw6lzdWx0YXRzIGltcG9ydMOpcyB2aWEgSHViJ2VhdSwgcGFyIHF1YWxpZmljYXRpb24NCg0KYGBge3IgY2FydGVfcmVzdWx0YXRzfQ0KcmVzdWx0YXRzX3NhZ2VzICU+JQ0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9zZihkYXRhPXNhZ2VzKSsNCmdlb21fc2YoYWVzKGNvbG9yPWxpYmVsbGVfcXVhbGlmaWNhdGlvbikpDQpgYGANCiMjIEV4ZW1wbGUgZGUgcsOpc3VsYXQgOiBDYXJ0ZSBkZSBsJ2luZGljZSBEaWF0b23DqWVzIChJQkQpDQpgYGB7ciBjYXJ0ZV9pbmRpY2V9DQpyZXN1bHRhdHNfc2FnZXMgJT4lDQogIGZpbHRlcihjb2RlX2luZGljZSA9PSAnNTg1NicpJT4lICM1ODU2IElCRCBJbmRpY2UgRGlhdG9tw6llcw0KICBnZ3Bsb3QoKSsNCiAgZ2VvbV9zZihkYXRhPXNhZ2VzKSsNCmdlb21fc2YoYWVzKGNvbG9yPXJlc3VsdGF0X2luZGljZSkpDQpgYGANCg0KIyBKb2ludHVyZSBhdmVjIGxlcyB0YWJsZXMgcsOpZsOpcmVudGllbHMNCg0KIyMgU2l0ZXMgaW5jb25udXMNCg0KTGVzIHNpdGVzIGltcG9ydMOpcyBzb250IGNvbXBhcsOpcyDDoCBsYSB0YWJsZSBkZXMgc2l0ZXMgYmFuY2FyaXPDqXMgcHLDqWPDqWRlbW1lbnQsIHBhciBjb2RlIFNBTkRSRS4NCg0KTGlzdGUgZGVzIHNpdGVzIGluY29ubnVzIDoNCg0KYGBge3Igc2l0ZXNfaW5jb25udXN9DQpzaXRlc19pbmNvbm51cyA8LSBzaXRlc19zYWdlcyAlPiUNCiAgIyB0YWJsZXMgZGVzIHNpdGVzDQogIGxlZnRfam9pbih0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsInNpdGUiKSksIGJ5PWMoImNvZGVfc3RhdGlvbl9oeWRyb2JpbyIgPSAiY29kZSIpLCBjb3B5PVRSVUUsIHN1ZmZpeCA9IGMoIiIsICIuc2l0ZSIpKSU+JQ0KICBmaWx0ZXIoaXMubmEoc2l0ZV9pZCkpDQoNCnNpdGVzX2luY29ubnVzICU+JSANCiAgZGlzdGluY3QoY29kZV9zdGF0aW9uX2h5ZHJvYmlvLCBsaWJlbGxlX3N0YXRpb25faHlkcm9iaW8pDQpgYGANCkNhcnRlIGRlcyBzaXRlcyBpbmNvbm51cyA6DQoNCmBgYHtyIGNhcnRlX3NpdGVzX2luY29ubnVzfQ0Kc2l0ZXNfaW5jb25udXMgJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX3NmKGRhdGE9c2FnZXMpKw0KZ2VvbV9zZihhZXMoY29sb3I9bGliZWxsZV9kZXBhcnRlbWVudCkpDQpgYGANCg0KIyMjIE1pc2UgZW4gZm9ybWUgZGVzIG5vdXZlYXV4IHNpdGVzIGVuIGZvbmN0aW9uIGR1IHNjaGVtYSBkZSBsYSB0YWJsZSBkZXMgc2l0ZXMgYmFuY2FyaXPDqXMNCg0KYGBge3IgdGFibGUgc2l0ZXNfaW5jb25udXN9DQoNCmluc2VydF9zaXRlcyA8LSBzaXRlc19pbmNvbm51cyAlPiUNCiAgbXV0YXRlKGNvb3JkX3ggPSBzdF9jb29yZGluYXRlcyhzdF90cmFuc2Zvcm0oZ2VvbWV0cnksIDIxNTQpKVssMV0sDQogICAgICAgICBjb29yZF95ID0gc3RfY29vcmRpbmF0ZXMoc3RfdHJhbnNmb3JtKGdlb21ldHJ5LCAyMTU0KSlbLDJdLA0KICAgICAgICAgbG9uZ2l0dWRlX3dnczg0ID0gc3RfY29vcmRpbmF0ZXMoc3RfdHJhbnNmb3JtKGdlb21ldHJ5LCA0MzI2KSlbLDFdLA0KICAgICAgICAgbGF0aXR1ZGVfd2dzODQgPSBzdF9jb29yZGluYXRlcyhzdF90cmFuc2Zvcm0oZ2VvbWV0cnksIDQzMjYpKVssMl0sDQogICAgICAgICB0eXBlc2l0ZV9pZCA9ICcxJywNCiAgICAgICAgIHByb2plY3Rpb25faWQgPSAnMjE1NCcsDQogICAgICAgICBzb3VyY2UgPSAnT0ZCL05BSUFERVMnLA0KICAgICAgICAgbWFqID0gZm9ybWF0KFN5cy5EYXRlKCksIiVZLSVtLSVkIikNCiAgICAgICAgICklPiUgDQogIGFzX3RpYmJsZSgpICU+JQ0Kc2VsZWN0KGNvZGUgPSBjb2RlX3N0YXRpb25faHlkcm9iaW8sDQpsaWJlbGxlID0gbGliZWxsZV9zdGF0aW9uX2h5ZHJvYmlvLA0KdHlwZXNpdGVfaWQsDQpjb29yZF94LA0KY29vcmRfeSwNCnByb2plY3Rpb25faWQsDQpsb25naXR1ZGVfd2dzODQsDQpsYXRpdHVkZV93Z3M4NCwNCnNvdXJjZSwNCm1haikNCg0KaW5zZXJ0X3NpdGVzDQoNCmBgYA0KIyMjIEluc2VydGlvbiBkZXMgbm91dmVhdXggc2l0ZXMgDQoNCmBgYHtyIGluc2VydCBzaXRlc19pbmNvbm51cywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCg0Kc2Y6OmRiV3JpdGVUYWJsZShjb25uID0gY29uX3Bvc3RncmVzcWxfZGV2LCBuYW1lID0gSWQoc2NoZW1hID0gImVhdV9zdHJ1Y3R1cmUiLHRhYmxlID0gInNpdGUiKSwgdmFsdWUgPSBpbnNlcnRfc2l0ZXMsIG92ZXJ3cml0ZT1GQUxTRSwgYXBwZW5kPVRSVUUsIGZpbGVFbmNvZGluZz0ibGF0aW4xIikNCmBgYA0KDQojIyBUYWJsZSBkZSBjb3JyZXNwb25kYW5jZSBzaXRlIC8gdGVycml0b2lyZXMgKEVHQSAtIEVudGl0w6lzIEdlb2dyYXBoaXF1ZXMgZXQgQWRtaW5pc3RyYXRpdmVzKQ0KDQpMYSB0YWJsZSBkZSBjb3JyZXNwb25kYW5jZSBlc3QgaXNzdWUgZGUgbGEgam9pbnR1cmUgc3BhdGlhbGUgZW50cmUgbGEgdGFibGUgZGVzIHNpdGVzIGV0IGxlcyBkaWZmw6lyZW50ZXMgw6ljaGVsbGVzIGRlIHRlcnJpdG9pcmVzIGJyZXRvbnMgOiBSw6lnaW9uLCBEw6lwYXJ0ZW1lbnRzLCBFUENJLCBTQUdFLCBDb250cmF0cyBkZSB0ZXJyaXRvaXJlcw0KDQpgYGB7ciBpbXBvcnRfZWF1X2NvcnJlc3BvbmRhbmNlX3NpdGVfZWdhfQ0KY29ycmVzcG9uZGFuY2Vfc2l0ZV9lZ2EgPC0gdGJsKGNvbl9wb3N0Z3Jlc3FsX2RldiwgZGJwbHlyOjppbl9zY2hlbWEoImVhdV9yZWZlcmVudGllbCIsImdlb19jb3JyZXNwb25kYW5jZV9zaXRlX2VnYSIpKSU+JQ0KICBtdXRhdGUodHlwZXNpdGUgPSAnU0lURScpDQoNCmNvcnJlc3BvbmRhbmNlX3NpdGVfZWdhDQpgYGANCiMjIExpc3RlIGRlcyBwYXJhbcOodHJlcyBiYW5jYXJpc8Opcw0KDQpMYSBsaXN0ZSBkZXMgcGFyYW3DqHRyZXMgZXN0IGlzc3VlIGR1IGRpY3Rpb25uYWlyZSBuYXRpb25hbCBkZXMgZG9ubsOpZXMgc3VyIGwnZWF1IChTQU5EUkUpLg0KDQpgYGB7ciBpbXBvcnQgcGFyYW1ldHJlc30NCg0KbGlzdGVfcGFyYW1ldHJlcyA8LSBhcy5saXN0KHN0cnNwbGl0KHBhcmFtcyRjb2Rlc19wYXJhbWV0cmVzLCAiLCIpW1sxXV0pDQoNCnBhcmFtZXRyZXMgPC0gdGJsKGNvbl9wb3N0Z3Jlc3FsX2RldiwgZGJwbHlyOjppbl9zY2hlbWEoImVhdV9zdHJ1Y3R1cmUiLCJwYXJhbWV0cmUiKSklPiUNCiAgY29sbGVjdCgpICU+JQ0KICBmaWx0ZXIoY29kZSAlaW4lIGxpc3RlX3BhcmFtZXRyZXMpDQoNCnBhcmFtZXRyZXMNCmBgYA0KDQojIyBTZXVpbHMgZGUgcXVhbGl0w6kgRENFDQoNCkxhIGxpc3RlIGRlcyBzZXVpbHMgZGUgcXVhbGl0w6kgZXN0IHByb2R1aXRlIMOgIHBhcnRpciBkZSBsJ1tBcnLDqnTDqSBkdSAyNyBqdWlsbGV0IDIwMThdKGh0dHBzOi8vd3d3LmxlZ2lmcmFuY2UuZ291di5mci9qb3JmL2FydGljbGVfam8vSk9SRkFSVEkwMDAwMzczNDc3ODIpIGRpc3BvbmlibGUgc3VyIExlZ2lmcmFuY2VbXmZvb3Rub3RlXSANCg0KW15mb290bm90ZV0gW0FycsOqdMOpIGR1IDI3IGp1aWxsZXQgMjAxOCBtb2RpZmlhbnQgbCdhcnLDqnTDqSBkdSAyNSBqYW52aWVyIDIwMTAgcmVsYXRpZiBhdXggbcOpdGhvZGVzIGV0IGNyaXTDqHJlcyBkJ8OpdmFsdWF0aW9uIGRlIGwnw6l0YXQgw6ljb2xvZ2lxdWUsIGRlIGwnw6l0YXQgY2hpbWlxdWUgZXQgZHUgcG90ZW50aWVsIMOpY29sb2dpcXVlIGRlcyBlYXV4IGRlIHN1cmZhY2UgcHJpcyBlbiBhcHBsaWNhdGlvbiBkZXMgYXJ0aWNsZXMgUi4gMjEyLTEwLCBSLiAyMTItMTEgZXQgUi4gMjEyLTE4IGR1IGNvZGUgZGUgbCdlbnZpcm9ubmVtZW50IC0gTMOpZ2lmcmFuY2UgKGxlZ2lmcmFuY2UuZ291di5mcildKGh0dHBzOi8vd3d3LmxlZ2lmcmFuY2UuZ291di5mci9qb3JmL2FydGljbGVfam8vSk9SRkFSVEkwMDAwMzczNDc3ODIpDQoNCmBgYHtyIGltcG9ydCBzZXVpbHMgZGUgcXVhbGl0w6l9DQoNCmNsYXNzZXNfcXVhbGl0ZSA8LSB0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsImpvaW5fcGFyYW1ldHJlX2NsYXNzZSIpKSU+JQ0KICBsZWZ0X2pvaW4odGJsKGNvbl9wb3N0Z3Jlc3FsX2RldiwgZGJwbHlyOjppbl9zY2hlbWEoImVhdV9zdHJ1Y3R1cmUiLCJjbGFzc2UiKSksIGJ5PSJjbGFzc2VfaWQiKSU+JQ0KICBsZWZ0X2pvaW4oIHNlbGVjdCh0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsInBhcmFtZXRyZSIpKSxwYXJhbWV0cmVfaWQsbGliZWxsZSwgY29kZSksIGJ5PSJwYXJhbWV0cmVfaWQiKSU+JQ0KICBjb2xsZWN0KCklPiUNCiAgZmlsdGVyKHBhcmFtZXRyZV9pZCAlaW4lIHBhcmFtZXRyZXMkcGFyYW1ldHJlX2lkLA0KICAgICAgICAgdmFsaWRlID09IDEpDQoNCmNsYXNzZXNfcXVhbGl0ZSAlPiUNCiAgc2VsZWN0KGxpYmVsbGUueSwgY29kZS55LCBwYXJhbWV0cmVfaWQsIGxpYmVsbGVfY291cnQsIGNvZGUueCwgYm9ybmVfaW5mX2luY2x1ZSwgYm9ybmVfc3VwX2V4Y2x1ZSwgc291cmNlKQ0KYGBgDQoNCiMjIFRhYmxlIGNvbXBsw6h0ZQ0KDQpUYWJsZSBkZXMgcsOpc3VsdGF0cyBhdmVjIGxlcyBhdHRyaWJ1dHMgZGVzIHLDqWbDqXJlbnRpZWxzDQoNCmBgYHtyIHRhYmxlIGluZGljZXN9DQp0YWJsZV9pbmRpY2VzIDwtIHJlc3VsdGF0c19zYWdlcyAlPiUNCiAgbXV0YXRlKENvb3JkWF9XR1M4NCA9IHN0X2Nvb3JkaW5hdGVzKHN0X3RyYW5zZm9ybShnZW9tZXRyeSwgNDMyNikpWywxXSwNCiAgICAgICAgIENvb3JkWV9XR1M4NCA9IHN0X2Nvb3JkaW5hdGVzKHN0X3RyYW5zZm9ybShnZW9tZXRyeSwgNDMyNikpWywyXSwNCiAgICAgICAgIGRhdGVfcHJlbGV2ZW1lbnQgPSBhcy5EYXRlKGRhdGVfcHJlbGV2ZW1lbnQpDQogICAgICAgICApJT4lDQogIGFzX3RpYmJsZSgpJT4lDQogICMgdGFibGVzIGRlcyBwYXJhbcOodHJlcw0KICBsZWZ0X2pvaW4ocGFyYW1ldHJlcywgYnk9YygiY29kZV9pbmRpY2UiPSJjb2RlIiksIHN1ZmZpeCA9IGMoIiIsICIucGFyYW1ldHJlIikpJT4lDQogICMgVW5pdMOpIGluY29ubnVlIC0tPiBjb2RlIHNhbmRyZSAwDQogICMgbiAobm9tYnJlKSAtLT4gY29kZSBzYW5kcmUgMjE0DQogICMg4oCwIHZzIFNNT1cgLS0+IGNvZGUgc2FuZHJlIDMyDQogIG11dGF0ZSh1bml0ZV9jb2RlID0gY2FzZV93aGVuKHVuaXRlX2luZGljZSA9PSAiVW5pdMOpIGluY29ubnVlIiB+ICcwJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bml0ZV9pbmRpY2UgPT0gIm4iIH4gJzIxNCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pdGVfaW5kaWNlID09ICLigLAgdnMgU01PVyIgfiAnMzInLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiB1bml0ZV9pbmRpY2UpLA0KICAgICAgICAgcmVzdWx0YXRfaW5kaWNlID0gaWZlbHNlKHJlc3VsdGF0X2luZGljZSA9PSA5OTksIE5BLCByZXN1bHRhdF9pbmRpY2UpKSAlPiUNCiAgIyB0YWJsZXMgZGVzIHVuaXTDqXMNCiAgbGVmdF9qb2luKHRibChjb25fcG9zdGdyZXNxbF9kZXYsIGRicGx5cjo6aW5fc2NoZW1hKCJlYXVfcmVmZXJlbnRpZWwiLCJ1bml0ZSIpKSwgYnk9YygidW5pdGVfY29kZSIgPSAiY29kZSIpLCBjb3B5PVRSVUUsIHN1ZmZpeCA9IGMoIiIsICIudW5pdGUiKSklPiUNCiAgIyB0YWJsZXMgZGVzIHNpdGVzDQogIGxlZnRfam9pbih0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsInNpdGUiKSksIGJ5PWMoImNvZGVfc3RhdGlvbl9oeWRyb2JpbyIgPSAiY29kZSIpLCBjb3B5PVRSVUUsIHN1ZmZpeCA9IGMoIiIsICIuc2l0ZSIpKQ0KDQp0YWJsZV9pbmRpY2VzIA0KYGBgDQojIyMgU3ludGjDqHNlIGRlcyBkb25uw6llcyBtYW5xdWFudGVzDQoNCmBgYHtyIGRvbm5lZXNfbWFucXVhbnRlc30NCiMgU3ludGjDqHNlIGRlcyBkb25uw6llcyBtYW5xdWFudGVzDQp0YWJsZV9pbmRpY2VzICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbmJfbGlnbmVzID0gbigpLA0KICAgIGxpZ25lc19zYW5zX3BhcmFtZXRyZSA9IHN1bShpcy5uYShwYXJhbWV0cmVfaWQpKSwNCiAgICBsaWduZXNfc2Fuc191bml0ZSA9IHN1bShpcy5uYSh1bml0ZV9pZCkpLA0KICAgIGxpZ25lc19zYW5zX3NpdGVzID0gc3VtKGlzLm5hKHNpdGVfaWQpKQ0KICApDQoNCiMgVW5pdMOpcyBpbmNvbm51ZXMgZGUgbGEgdGFibGUgZGUgcsOpZsOpcmVuY2UNCnRhYmxlX2luZGljZXMgJT4lIGZpbHRlcihpcy5uYSh1bml0ZV9pZCkpICU+JSBkaXN0aW5jdCh1bml0ZV9pbmRpY2UpDQoNCiMgU3RhdGlvbnMgaW5jb25udWVzIGRlIGxhIHRhYmxlIGRlcyBzaXRlcw0KdGFibGVfaW5kaWNlcyAlPiUgZmlsdGVyKGlzLm5hKHNpdGVfaWQpKSAlPiUgZGlzdGluY3QobGliZWxsZV9zdGF0aW9uX2h5ZHJvYmlvKQ0KDQojIExpc3RlIGRlcyBjb2RlcyBpbmRpY2VzDQp0YWJsZV9pbmRpY2VzICU+JSBkaXN0aW5jdChjb2RlX2luZGljZSkNCg0KYGBgDQoNCiMjIENsYXNzZXMgZGVzIGluZGljZXMNCg0KQXR0cmlidXRpb24gZGVzIGNsYXNzZXMgZGUgcXVhbGl0w6kgYXV4IHLDqXN1bHRhdHMgcGFyIHBhcmFtw6h0cmUgKGluZGljZSBiaW9sb2dpcXVlKQ0KDQpgYGB7ciBjbGFzc2VfaW5kaWNlc30NCnRhYmxlX2luZGljZXNfY2xhc3NlcyA8LSB0YWJsZV9pbmRpY2VzICU+JSANCiAgbGVmdF9qb2luKGNsYXNzZXNfcXVhbGl0ZSwgYnkgPSBjKCJwYXJhbWV0cmVfaWQiKSwgY29weSA9IFRSVUUsIHN1ZmZpeD1jKCIiLCIuY2xhc3NlIikpICU+JQ0KICBmaWx0ZXIocmVzdWx0YXRfaW5kaWNlIDwgYm9ybmVfc3VwX2V4Y2x1ZSAmIHJlc3VsdGF0X2luZGljZSA+PSBib3JuZV9pbmZfaW5jbHVlKQ0KDQp0YWJsZV9pbmRpY2VzX2NsYXNzZXMNCmBgYA0KDQojIEV4cGxvcmF0aW9uIGRlcyBkb25uw6llcw0KDQojIyBEb25uw6llcyBkaXNwb25pYmxlcyBwYXIgYW4gZXQgcGFyIGluZGljZQ0KDQpgYGB7ciBncmFwaGVfaW5kaWNlcywgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTZ9DQp0YWJsZV9pbmRpY2VzX2NsYXNzZXMgJT4lDQogIGdyb3VwX2J5KHllYXIoZGF0ZV9wcmVsZXZlbWVudCksIGxpYmVsbGVfc3VwcG9ydCwgbGliZWxsZV9pbmRpY2UpICU+JQ0KICBzdW1tYXJpc2UoTmJfcmVzdWx0YXRzID0gbigpLA0KICAgICAgICAgICAgQW5uZWUgPSB5ZWFyKGRhdGVfcHJlbGV2ZW1lbnQpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gYXMuZmFjdG9yKEFubmVlKSwgeT1OYl9yZXN1bHRhdHMsIGZpbGw9bGliZWxsZV9pbmRpY2UpKSsNCiAgICAgICAgICAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSsNCiAgZmFjZXRfZ3JpZChsaWJlbGxlX3N1cHBvcnR+LikNCiAgDQpgYGANCg0KIyMgRGlzdHJpYnV0aW9uIGRlcyByw6lzdWx0YXRzIHBhciBpbmRpY2UgZXQgcGFyIGNsYXNzZSBkZSBxdWFsaXTDqQ0KDQpgYGB7ciBncmFwaGVfaW5kaWNlc19jbGFzc2VzLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xNn0NCnRhYmxlX2luZGljZXNfY2xhc3NlcyAlPiUNCiAgYXJyYW5nZShjb2RlLngpJT4lDQogIGdncGxvdChhZXMoeCA9IGNvZGUueCwgeT1yZXN1bHRhdF9pbmRpY2UsIGZpbGwgPSBjb2RlLngpKSsNCiAgICAgICAgICAgZ2VvbV9ib3hwbG90KCkrDQogIGZhY2V0X3dyYXAofmxpYmVsbGVfaW5kaWNlLCBzY2FsZXMgPSAiZnJlZSIpDQogIA0KdGFibGVfaW5kaWNlc19jbGFzc2VzDQpgYGANCg0KIyBUcmFuc2Zvcm1hdGlvbiBkZXMgZG9ubsOpZXMNCg0KIyMgRG9ubsOpZXMgYW5udWVsbGVzDQoNCkRvbm7DqWVzIGFnZ3LDqWfDqWVzIHBhciBhbm7DqWUgZCdhbmFseXNlIDoNCg0KLSBJbmRpY2VzIG1veWVucw0KLSBDbGFzc2VzIG1heGltYWxlcw0KDQpgYGB7ciB0YWJsZV9pbmRpY2VzX2NsYXNzZXNfYW5uZWV9DQoNCnRhYmxlX2luZGljZXNfY2xhc3Nlc19hbm5lZSA8LSB0YWJsZV9pbmRpY2VzX2NsYXNzZXMgJT4lDQogIG11dGF0ZShBbm5lZSA9IHllYXIoYXMuRGF0ZShkYXRlX3ByZWxldmVtZW50KSkpICU+JQ0KICBncm91cF9ieShjb2RlX3N0YXRpb25faHlkcm9iaW8sDQogICAgICAgICAgIGxvbmdpdHVkZV93Z3M4NCwNCiAgICAgICAgICAgbGF0aXR1ZGVfd2dzODQsDQogICAgICAgICAgIGxpYmVsbGVfc3VwcG9ydCwNCiAgICAgICAgICAgY29kZV9pbmRpY2UsDQogICAgICAgICAgIHBhcmFtZXRyZV9pZCwNCiAgICAgICAgICAgbGliZWxsZSwNCiAgICAgICAgICAgc3ltYm9sZSwNCiAgICAgICAgICAgQW5uZWUpICU+JQ0KICBzdW1tYXJpc2UoY2xhc3NlID0gYXMuaW50ZWdlcihtYXgoY29kZS54KSksIA0KICAgICAgICAgICAgcmVzdWx0YXRfaW5kaWNlID0gbWVhbihyZXN1bHRhdF9pbmRpY2UpLA0KICAgICAgICAgICAgcmVzdWx0YXRfcXVhbGlmaWNhdGlvbiA9IGFzLmludGVnZXIobWF4KGNvZGVfcXVhbGlmaWNhdGlvbikpKQ0KDQp0YWJsZV9pbmRpY2VzX2NsYXNzZXNfYW5uZWUNCmBgYA0KDQojIyBDbGFzc2UgZGUgcXVhbGl0w6kgYmlvbG9naXF1ZSBnbG9iYWxlIA0KDQpDYWxjdWwgZCd1bmUgY2xhc3NlIGRlICpxdWFsaXTDqSBiaW9sb2dpcXVlIGdsb2JhbGUqIChtYXhpbXVtIGRlcyBjbGFzc2VzIGRlcyBpbmRpY2VzIGFubnVlbHMpDQoNCmBgYHtyfQ0KdGFibGVfaW5kaWNlc19jbGFzc2VfZ2xvYmFsZV9hbm5lZSA8LSB0YWJsZV9pbmRpY2VzX2NsYXNzZXNfYW5uZWUgJT4lDQogIGdyb3VwX2J5KGNvZGVfc3RhdGlvbl9oeWRyb2JpbywgbG9uZ2l0dWRlX3dnczg0LCBsYXRpdHVkZV93Z3M4NCwgQW5uZWUpJT4lDQogIHN1bW1hcmlzZShSZXN1bHRhdCA9IG1heChjbGFzc2UpKSU+JQ0KICBtdXRhdGUoU2VyaWUgPSAnQ2xhc3NlIC0gUXVhbGl0w6kgYmlvbG9naXF1ZSBHbG9iYWxlJywgDQogICAgICAgICBsaWJlbGxlX3N1cHBvcnQgPSAnUXVhbGl0w6kgYmlvbG9naXF1ZSBHbG9iYWxlJywNCiAgICAgICAgIHN5bWJvbGUgPSAnWCcpDQoNCnRhYmxlX2luZGljZXNfY2xhc3NlX2dsb2JhbGVfYW5uZWUNCmBgYA0KDQojIEltcG9ydCBkZXMgZG9ubsOpZXMgZW4gYmFzZQ0KDQojIyBEb25uw6llcyBhdSBmb3JtYXQgZGUgbGEgdGFibGUgZWF1X3N0cnVjdHVyZS5hbmFseXNlX2Jpb19lc3UgLSBzZXJ2ZXVyIFBvc3RncmVTUUwgREVWDQoNCiMjIyBNaXNlIGVuIGZvcm1lIGRlIGxhIHRhYmxlDQoNCmBgYHtyIGFuYWx5c2VfYmlvX2VzdX0NCg0KYW5hbHlzZV9iaW9fZXN1IDwtIHRhYmxlX2luZGljZXMgJT4lDQogIGZpbHRlcighaXMubmEocmVzdWx0YXRfaW5kaWNlKSklPiUNCiAgbGVmdF9qb2luKHNlbGVjdCh0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsImRhdGUiKSksIGRhdGVfaWQsIGRhdGVfZHVfam91ciksIGJ5PWMoImRhdGVfcHJlbGV2ZW1lbnQiID0gImRhdGVfZHVfam91ciIpLCBjb3B5PVRSVUUpJT4lDQogIGxlZnRfam9pbihzZWxlY3QodGJsKGNvbl9wb3N0Z3Jlc3FsX2RldiwgZGJwbHlyOjppbl9zY2hlbWEoImVhdV9zdHJ1Y3R1cmUiLCJzdXBwb3J0IikpLCBzdXBwb3J0X2lkLCBjb2RlKSwgYnk9YygiY29kZV9zdXBwb3J0IiA9ICJjb2RlIiksIGNvcHk9VFJVRSklPiUNCiAgbGVmdF9qb2luKHNlbGVjdCh0YmwoY29uX3Bvc3RncmVzcWxfZGV2LCBkYnBseXI6OmluX3NjaGVtYSgiZWF1X3N0cnVjdHVyZSIsInJlbWFycXVlIikpLCByZW1hcnF1ZV9pZCwgY29kZSksIGJ5PWMoImNvZGVfcXVhbGlmaWNhdGlvbiIgPSAiY29kZSIpLCBjb3B5PVRSVUUpJT4lDQogIG11dGF0ZShwcmVsZXZlbWVudF9jb2RlID0gcGFzdGUwKGNvZGVfc3RhdGlvbl9oeWRyb2JpbyxkYXRlX2lkKSwNCiAgICAgICAgIHJkZF9pZCA9IDAsDQogICAgICAgICBtaWxpZXVfaWQgPSAzLA0KICAgICAgICAgZnJhY3Rpb25faWQgPSAyMiwNCiAgICAgICAgIGxpbWl0ZV9xdWFudGlmaWNhdGlvbiA9IDAsDQogICAgICAgICBzb3VyY2UgPSAnT0ZCL05BSUFERVMnLA0KICAgICAgICAgbWFqID0gZm9ybWF0KFN5cy5EYXRlKCksIiVZLSVtLSVkIikpJT4lDQogIHNlbGVjdChzaXRlX2lkLA0KICAgICAgICAgZGF0ZV9pZCwNCiAgICAgICAgIHJkZF9pZCwNCiAgICAgICAgIHByZWxldmVtZW50X2NvZGUsDQogICAgICAgICBtaWxpZXVfaWQsDQogICAgICAgICBzdXBwb3J0X2lkLA0KICAgICAgICAgZnJhY3Rpb25faWQsDQogICAgICAgICBwYXJhbWV0cmVfaWQsDQogICAgICAgICByZXN1bHRhdCA9IHJlc3VsdGF0X2luZGljZSwNCiAgICAgICAgIHJlbWFycXVlX2lkLA0KICAgICAgICAgbGltaXRlX3F1YW50aWZpY2F0aW9uLA0KICAgICAgICAgc291cmNlLA0KICAgICAgICAgbWFqKQ0KDQphbmFseXNlX2Jpb19lc3UNCmBgYA0KDQojIyMgU8OpbGVjdGlvbiBkZXMgbm91dmVsbGVzIGxpZ25lcyDDoCBpbnPDqXJlcg0KDQpgYGB7ciBpbnNlcnRfYW5hbHlzZV9iaW9fZXN1fQ0KIyBUT0RPIGNvbXBhcmFpc29uIGF2ZWMgbGEgYmFzZQ0KaW5zZXJ0X2FuYWx5c2VfYmlvX2VzdSA8LSBhbmFseXNlX2Jpb19lc3UNCg0KYGBgDQoNCiMjIyBJbnNlcnRpb24gZGVzIG5vdXZlbGxlcyBsaWduZXMNCg0KYGBge3IgaW5zZXJ0IHJlc3VsdGF0c19pbmNvbm51cywgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCmRiRXhlY3V0ZShjb25fcG9zdGdyZXNxbF9kZXYsICJUUlVOQ0FURSBUQUJMRSBlYXVfc3RydWN0dXJlLmFuYWx5c2VfYmlvX2VzdSIpDQoNCmRiQXBwZW5kVGFibGUoY29ubiA9IGNvbl9wb3N0Z3Jlc3FsX2RldiwgbmFtZSA9IElkKHNjaGVtYSA9ICJlYXVfc3RydWN0dXJlIix0YWJsZSA9ICJhbmFseXNlX2Jpb19lc3UiKSwgdmFsdWUgPSBpbnNlcnRfYW5hbHlzZV9iaW9fZXN1LCBmaWxlRW5jb2Rpbmc9ImxhdGluMSIpDQpgYGANCg0KIyMgRG9ubsOpZXMgYXUgZm9ybWF0IGRlIGxhIHRhYmxlIGVhdV90Ymkub2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UgLSBzZXJ2ZXVyIE1hcmlhREIgT0VCDQoNCmBgYHtyIHNlcmllc19pbmRpY2VzX2FubnVlbHN9DQoNCnRhYmxlX3Nlcmllc19pbmRpY2VzIDwtIHRhYmxlX2luZGljZXNfY2xhc3Nlc19hbm5lZSAlPiUgDQogIG11dGF0ZShpbmRpY2UgPSBwYXN0ZShsaWJlbGxlLCBjb2RlX2luZGljZSwgc2VwPScgLSAnKSklPiUNCiAgcGl2b3RfbG9uZ2VyKGNvbHM9IGMoImNsYXNzZSIsInJlc3VsdGF0X2luZGljZSIsInJlc3VsdGF0X3F1YWxpZmljYXRpb24iKSklPiUNCiAgbXV0YXRlKFNlcmllID0gY2FzZV93aGVuKG5hbWUgPT0gJ2NsYXNzZScgfiBwYXN0ZSgnQ2xhc3NlJyxsaWJlbGxlX3N1cHBvcnQsc2VwID0gJyAtICcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9PSAncmVzdWx0YXRfaW5kaWNlJyB+IHBhc3RlKCdJbmRpY2UnLGluZGljZSxzZXAgPSAnIC0gJyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID09ICdyZXN1bHRhdF9xdWFsaWZpY2F0aW9uJyB+IHBhc3RlKCdRdWFsaWZpY2F0aW9uJyxpbmRpY2Usc2VwID0gJyAtICcpDQogICAgICAgICAgICAgICAgICAgICAgICAgICApDQogICAgICAgICApJT4lDQogIGdyb3VwX2J5KGNvZGVfc3RhdGlvbl9oeWRyb2JpbywNCiAgICAgICAgICAgbG9uZ2l0dWRlX3dnczg0LA0KICAgICAgICAgICBsYXRpdHVkZV93Z3M4NCwNCiAgICAgICAgICAgbGliZWxsZV9zdXBwb3J0LA0KICAgICAgICAgICBTZXJpZSwNCiAgICAgICAgICAgc3ltYm9sZSwNCiAgICAgICAgICAgQW5uZWUpJT4lDQogIHN1bW1hcmlzZShSZXN1bHRhdCA9IG1heCh2YWx1ZSkpJT4lDQogIHVuZ3JvdXAoKSU+JQ0KICB1bmlvbih0YWJsZV9pbmRpY2VzX2NsYXNzZV9nbG9iYWxlX2FubmVlKQ0KDQp0YWJsZV9zZXJpZXNfaW5kaWNlcw0KYGBgDQojIyMgRMOpY2xpbmFpc29uIHBhciBjb21iaW5haXNvbiBTSVRFIC8gRUdBDQoNCkxhIHRhYmxlIGRlIHZhbG9yaXNhdGlvbiBkw6ljbGluZSBsZXMgcsOpc3VsdGF0cyBwb3VyIGNoYXF1ZSDDqWNoZWxsZSBkZSB0ZXJyaXRvaXJlDQoNCmBgYHtyIHRhYmxlX3Nlcmllc19pbmRpY2VzX2VnYX0NCnRhYmxlX3Nlcmllc19pbmRpY2VzX2VnYSA8LSB0YWJsZV9zZXJpZXNfaW5kaWNlcyU+JQ0KICAjIHRhYmxlIGRlcyBjb3JyZXNwb25kYW5jZXMgc2l0ZXMgLyBVR0ENCiAgbGVmdF9qb2luKGNvcnJlc3BvbmRhbmNlX3NpdGVfZWdhLCBieT1jKCJjb2RlX3N0YXRpb25faHlkcm9iaW8iID0gImNkc2l0ZSIpLCBjb3B5ID0gVFJVRSklPiUNCiAgbGVmdF9qb2luKHNpdGVzX3JjcywgYnk9ImNvZGVfc3RhdGlvbl9oeWRyb2JpbyIpDQoNCnRhYmxlX3Nlcmllc19pbmRpY2VzX2VnYQ0KYGBgDQojIyMgTWlzZSBlbiBmb3JtZSBkZSBsYSB0YWJsZQ0KDQpgYGB7ciB0YWJsZSBvZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZX0NCg0Kb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UgPC0gdGFibGVfc2VyaWVzX2luZGljZXNfZWdhICU+JQ0KICBtdXRhdGUoUGVyaW9kZSA9IGFzLmNoYXJhY3RlcihBbm5lZSksDQogICAgICAgICBTb3VyY2UgPSAnT0ZCL05BSUFERVMnLA0KICAgICAgICAgTWlzZV9hX2pvdXIgPSBmb3JtYXQoU3lzLkRhdGUoKSwiJVktJW0tJWQiKQ0KICAgICAgICAgKSU+JSANCiAgc2VsZWN0KFR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlID0gdHlwZXNpdGUsDQogICAgICAgICBDb2RlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZSA9IGNvZGVfc3RhdGlvbl9oeWRyb2JpbywNCiAgICAgICAgIExpYmVsbGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlID0gbGJzaXRlLA0KICAgICAgICAgQ29vcmRYX1dHUzg0ID0gbG9uZ2l0dWRlX3dnczg0LA0KICAgICAgICAgQ29vcmRZX1dHUzg0ID0gbGF0aXR1ZGVfd2dzODQsDQogICAgICAgICBSZXNlYXVfUkNTID0gaW5jbHVzX3JjcywNCiAgICAgICAgIFR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlX2Fzc29jaWVlID0gdHlwZWVnYSwNCiAgICAgICAgIENvZGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlX2Fzc29jaWVlID0gY2RlZ2EsDQogICAgICAgICBMaWJlbGxlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZV9hc3NvY2llZSA9IGxiZWdhLA0KICAgICAgICAgUGVyaW9kZSwNCiAgICAgICAgIFNlcmllLA0KICAgICAgICAgdW5pdGUgPSBzeW1ib2xlLA0KICAgICAgICAgUmVzdWx0YXQsDQogICAgICAgICBTb3VyY2UsDQogICAgICAgICBNaXNlX2Ffam91cg0KICApDQoNCm9lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlDQoNCmBgYA0KIyMjIFPDqWxlY3Rpb24gZGVzIGxpZ25lcyBub3V2ZWxsZXMgw6AgaW5zw6lyZXINCg0KTGEgdGFibGUgZGUgcsOpc3VsdGF0cyBlc3QgY29tcGFyw6llIGF1eCBkb25uw6llcyBkw6lqw6AgYmFuY2FyaXPDqWVzIHBhciB1bmUgam9pbnR1cmUgZCdleGNsdXNpb24gKGFudGlfam9pbikuIExlcyBsaWduZXMgcmVzdGFudGVzIHBldXZlbnQgw6p0cmUgaW5zw6lyw6llcy4NCg0KYGBge3IgdGFibGUgaW5zZXJ0X29lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlfQ0KDQppbnNlcnRfb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UgPC0gb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UgJT4lIA0KICB1bmdyb3VwKCkgJT4lDQogICMgUmV0aXJlciBsZXMgbGlnbmVzIGRlcyByw6lzdWx0YXRzIGTDqWrDoCBleGlzdGFudHMgZGFucyBsYSB0YWJsZQ0KICBhbnRpX2pvaW4odGJsKGNvbl9lYXVfdGJpLCJvZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZSIsIGJ5PWMoIlR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlIiwgIkNvZGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlIiwgIlR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlX2Fzc29jaWVlIiwgIkNvZGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlX2Fzc29jaWVlIiwgIlBlcmlvZGUiLCAiU2VyaWUiKSksIGNvcHkgPSBUUlVFKSU+JQ0KICAjIFJldGlyZXIgbGVzIHN0YXRpb25zIGh5ZHJvIHNhbnMgRUdBIGlkZW50aWZpw6llDQogIGZpbHRlcighaXMubmEoVHlwZV9lbnRpdGl0ZV9nZW9ncmFwaGlxdWUpKSU+JQ0KICAjIFJldGlyZXIgbGVzIGxpZ25lcyBhdmVjIHVuZSB2YWxldXIgTkENCiAgZHJvcF9uYSgpDQoNCmluc2VydF9vZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZQ0KYGBgDQoNCiMjIyBJbnNlcnRpb24gZGVzIG5vdXZlbGxlcyBsaWduZXMNCg0KYGBge3IgaW5zZXJ0IG9lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlLCBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KZGJFeGVjdXRlKGNvbl9lYXVfdGJpLCAiVFJVTkNBVEUgVEFCTEUgb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2UiKQ0KDQpkYkFwcGVuZFRhYmxlKGNvbm4gPSBjb25fZWF1X3RiaSwgbmFtZSA9ICJvZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZSIsIHZhbHVlID0gaW5zZXJ0X29lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlLCBmaWxlRW5jb2Rpbmc9ImxhdGluMSIpDQpgYGANCg0KIyMgRXhwb3J0cyBDU1YgcG91ciBsYSBwdWJsaWNhdGlvbiBkdSBqZXUgZGUgZG9ubsOpZXMgc3VyIGxlcyBwb3J0YWlscyBvcGVuZGF0YQ0KDQojIyMgRXhwb3J0IHBvdXIgbGUgR0lERQ0KDQpGaWNoaWVyIHBvdXIgbGEgcHVibGljYXRpb24gZHUgamV1IGRlIGRvbm7DqWVzIHN1ciBsZSBwb3J0YWlsIE9FQg0KDQpgYGB7ciBleHBvcnQgb2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2V9DQojIERlcHVpcyBsYSBiYXNlIGRlIGRvbm7DqWVzDQojdGJsKGNvbl9lYXVfdGJpLCAib2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfY2VfbmV3IiklPiUNCiMgb3UgZGVwdWlzIGxhIHRhYmxlIGVuIG3DqW1vaXJlDQpvZWJfZWF1X3F1YWxpdGVfYmlvbG9naXF1ZV9jZSAlPiUgIA0Kd3JpdGUudGFibGUoZmlsZSA9IHBhc3RlMChwYXJhbXMkcGF0aF9kYXRhdml6LCJcXEdJREVcXCIsIm9lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2NlLmNzdiIpLCBxdW90ZSA9IFRSVUUsIHNlcCA9ICI7IiwNCiAgICAgICAgICAgIGVvbCA9ICJcbiIsIG5hID0gIiIsIGRlYyA9ICIsIiwNCiAgICAgICAgICAgIGZpbGVFbmNvZGluZyA9ICJVVEYtOCIpDQpgYGANCg0KIyMjIEV4cG9ydCBwb3VyIEdFT0INCg0KRmljaGllciBwb3VyIGxhIHB1YmxpY2F0aW9uIGR1IGpldSBkZSBkb25uw6llcyBzdXIgR2VvYnJldGFnbmUNCg0KIyMjIyBNaXNlIGVuIGZvcm1lIGRlIGxhIHRhYmxlIHBvdXIgbCdlbnNlbWJsZSBkZXMgcGFyYW3DqHRyZXMNCg0KYGBge3Igb2ViX2VhdV9xdWFsaXRlX2dlb2J9DQpvZWJfZWF1X3F1YWxpdGVfZ2VvYiA8LSB0YWJsZV9zZXJpZXNfaW5kaWNlcyAgJT4lDQogIHVuZ3JvdXAoKSU+JQ0KICBsZWZ0X2pvaW4oc2VsZWN0KHNpdGVzX3NhZ2VzLGNvZGVfc3RhdGlvbl9oeWRyb2JpbyxsaWJlbGxlX3N0YXRpb25faHlkcm9iaW8pLCBieT0iY29kZV9zdGF0aW9uX2h5ZHJvYmlvIikgICU+JQ0KICBtdXRhdGUoQ29kZV9lbnRpdGl0ZV9nZW9ncmFwaGlxdWUgPSBjb2RlX3N0YXRpb25faHlkcm9iaW8sDQogICAgICAgICBMaWJlbGxlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZSA9IGxpYmVsbGVfc3RhdGlvbl9oeWRyb2JpbywNCiAgICAgICAgIENvb3JkWF9XR1M4NCA9IGxvbmdpdHVkZV93Z3M4NCwNCiAgICAgICAgIENvb3JkWV9XR1M4NCA9IGxhdGl0dWRlX3dnczg0LA0KICAgICAgICAgU2VyaWUsDQogICAgICAgICB1bml0ZSA9IHN5bWJvbGUsDQogICAgICAgICBUeXBlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZSA9ICdTSVRFJywNCiAgICAgICAgIFNvdXJjZSA9ICdPRkIvTkFJQURFUycsDQogICAgICAgICBNaXNlX2Ffam91ciA9IGZvcm1hdChTeXMuRGF0ZSgpLCIlWS0lbS0lZCIpKSAlPiUNCiAgc2VsZWN0KFR5cGVfZW50aXRpdGVfZ2VvZ3JhcGhpcXVlLA0KICAgICAgICAgQ29kZV9lbnRpdGl0ZV9nZW9ncmFwaGlxdWUsDQogICAgICAgICBMaWJlbGxlX2VudGl0aXRlX2dlb2dyYXBoaXF1ZSwNCiAgICAgICAgIENvb3JkWF9XR1M4NCwNCiAgICAgICAgIENvb3JkWV9XR1M4NCwNCiAgICAgICAgIFNlcmllLA0KICAgICAgICAgdW5pdGUsDQogICAgICAgICBTb3VyY2UsDQogICAgICAgICBNaXNlX2Ffam91ciwNCiAgICAgICAgIEFubmVlLA0KICAgICAgICAgUmVzdWx0YXQpICU+JQ0KICBwaXZvdF93aWRlcih2YWx1ZXNfZnJvbSA9IFJlc3VsdGF0LCBuYW1lc19mcm9tID0gQW5uZWUsIG5hbWVzX3NvcnQ9VFJVRSkNCmBgYA0KDQojIyMjIEV4cG9ydCBwb3VyIGxlIHBhcmFtw6h0cmUgIlF1YWxpdMOpIGdsb2JhbGUiDQoNCg0KYGBge3IgZXhwb3J0IG9lYl9lYXVfcXVhbGl0ZV9iaW9sb2dpcXVlX2dsb2JhbGV9DQoNCm9lYl9lYXVfcXVhbGl0ZV9nZW9iICAlPiUNCiAgZmlsdGVyKFNlcmllID09ICdDbGFzc2UgLSBRdWFsaXTDqSBiaW9sb2dpcXVlIEdsb2JhbGUnKSAgJT4lDQp3cml0ZS50YWJsZShmaWxlID0gcGFzdGUwKHBhcmFtcyRwYXRoX2RhdGF2aXosIlxcR0VPQlxcIiwib2ViX2VhdV9xdWFsaXRlX2Jpb2xvZ2lxdWVfZ2xvYmFsZS5jc3YiKSwgcXVvdGUgPSBUUlVFLCBzZXAgPSAiOyIsDQogICAgICAgICAgICBlb2wgPSAiXG4iLCBuYSA9ICIiLCBkZWMgPSAiLCIsDQogICAgICAgICAgICBmaWxlRW5jb2RpbmcgPSAiVVRGLTgiKQ0KYGBgDQoNCiMjIyMgRXhwb3J0IHBvdXIgbGUgcGFyYW3DqHRyZSAiRGlhdG9tw6llcyINCg0KYGBge3IgZXhwb3J0IG9lYl9lYXVfcXVhbGl0ZV9kaWF0b21lZXN9DQoNCm9lYl9lYXVfcXVhbGl0ZV9nZW9iICU+JQ0KICBmaWx0ZXIoU2VyaWUgPT0gJ0NsYXNzZSAtIERpYXRvbcOpZXMgYmVudGhpcXVlcycpICU+JQ0Kd3JpdGUudGFibGUoZmlsZSA9IHBhc3RlMChwYXJhbXMkcGF0aF9kYXRhdml6LCJcXEdFT0JcXCIsIm9lYl9lYXVfcXVhbGl0ZV9kaWF0b21lZXMuY3N2IiksIHF1b3RlID0gVFJVRSwgc2VwID0gIjsiLA0KICAgICAgICAgICAgZW9sID0gIlxuIiwgbmEgPSAiIiwgZGVjID0gIiwiLA0KICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gIlVURi04IikNCmBgYA0KDQojIyMjIEV4cG9ydCBwb3VyIGxlIHBhcmFtw6h0cmUgIk1hY3JvaW52ZXJ0w6licsOpcyINCg0KYGBge3IgZXhwb3J0IG9lYl9lYXVfcXVhbGl0ZV9tYWNyb2ludmVydGVicmVzfQ0KDQpvZWJfZWF1X3F1YWxpdGVfZ2VvYiAlPiUNCiAgZmlsdGVyKFNlcmllID09ICdDbGFzc2UgLSBNYWNyb2ludmVydMOpYnLDqXMgYXF1YXRpcXVlcycpICU+JQ0Kd3JpdGUudGFibGUoZmlsZSA9IHBhc3RlMChwYXJhbXMkcGF0aF9kYXRhdml6LCJcXEdFT0JcXCIsIm9lYl9lYXVfcXVhbGl0ZV9tYWNyb2ludmVydGVicmVzLmNzdiIpLCBxdW90ZSA9IFRSVUUsIHNlcCA9ICI7IiwNCiAgICAgICAgICAgIGVvbCA9ICJcbiIsIG5hID0gIiIsIGRlYyA9ICIsIiwNCiAgICAgICAgICAgIGZpbGVFbmNvZGluZyA9ICJVVEYtOCIpDQpgYGANCg0KIyMjIyBFeHBvcnQgcG91ciBsZSBwYXJhbcOodHJlICJNYWNyb3BoeXRlcyINCg0KYGBge3IgZXhwb3J0IG9lYl9lYXVfcXVhbGl0ZV9tYWNyb3BoeXRlc30NCg0Kb2ViX2VhdV9xdWFsaXRlX2dlb2IgJT4lDQogIGZpbHRlcihTZXJpZSA9PSAnQ2xhc3NlIC0gTWFjcm9waHl0ZXMnKSAlPiUNCndyaXRlLnRhYmxlKGZpbGUgPSBwYXN0ZTAocGFyYW1zJHBhdGhfZGF0YXZpeiwiXFxHRU9CXFwiLCJvZWJfZWF1X3F1YWxpdGVfbWFjcm9waHl0ZXMuY3N2IiksIHF1b3RlID0gVFJVRSwgc2VwID0gIjsiLA0KICAgICAgICAgICAgZW9sID0gIlxuIiwgbmEgPSAiIiwgZGVjID0gIiwiLA0KICAgICAgICAgICAgZmlsZUVuY29kaW5nID0gIlVURi04IikNCmBgYA==